package org.jflame.web.spring.jackson;

import org.jflame.commons.util.ArrayHelper;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.BeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.PropertyFilter;
import com.fasterxml.jackson.databind.ser.PropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;

@JsonFilter("JacksonDynamicFilter")
@SuppressWarnings("deprecation")
public class JacksonDynamicFilter extends FilterProvider {

    private Map<Class<?>,Set<String>> includeMap = new HashMap<>();// 要类型和要排除的属性
    private Map<Class<?>,Set<String>> excludeMap = new HashMap<>();

    public void include(Class<?> type, String[] fields) {
        addToMap(includeMap, type, fields);
    }

    public void exclude(Class<?> type, String[] fields) {
        addToMap(excludeMap, type, fields);
    }

    private void addToMap(Map<Class<?>,Set<String>> map, Class<?> type, String[] fields) {
        Set<String> filedSet = new HashSet<>();
        if (ArrayHelper.isNotEmpty(fields)) {
            for (String filed : fields) {
                filedSet.add(filed.trim());
            }
        }
        map.put(type, filedSet);
    }

    @Override
    public BeanPropertyFilter findFilter(Object filterId) {
        throw new UnsupportedOperationException("Access to deprecated filters not supported");
    }

    @Override
    public PropertyFilter findPropertyFilter(Object filterId, Object valueToFilter) {

        return new SimpleBeanPropertyFilter() {

            @Override
            public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider prov,
                    PropertyWriter writer) throws Exception {
                if (apply(pojo.getClass(), writer.getName())) {
                    writer.serializeAsField(pojo, jgen, prov);
                } else if (!jgen.canOmitFields()) {
                    writer.serializeAsOmittedField(pojo, jgen, prov);
                }
            }
        };
    }

    boolean apply(Class<?> type, String name) {
        Set<String> includeFields = includeMap.get(type);
        Set<String> filterFields = excludeMap.get(type);
        if (includeFields != null && includeFields.contains(name)) {
            return true;
        } else if (filterFields != null && !filterFields.contains(name)) {
            return true;
        } else if (includeFields == null && filterFields == null) {
            return true;
        }
        return false;
    }

    public void addMix(final ObjectMapper objectMapper) {
        if (!includeMap.isEmpty()) {
            includeMap.keySet()
                    .forEach(i -> objectMapper.addMixIn(i, JacksonDynamicFilter.class));
        }
        if (!excludeMap.isEmpty()) {
            excludeMap.keySet()
                    .forEach(i -> objectMapper.addMixIn(i, JacksonDynamicFilter.class));
        }
    }

}
